#include "Index.h"
#include <sstream>
#include <iomanip>
#include "../compresores/CodigoDelta.h"

using std::stringstream;
using std::hex;

Index::Index (string indexFile, string rutaCarpetaIdx) {
	string paraDocData(rutaCarpetaIdx+DOC_DATA);
	string paraDocFC(rutaCarpetaIdx+DOC_F_C);
	string paraDocWords(rutaCarpetaIdx+DOC_WORDS_POINTERS);
	wordsNumber = 0;
	v_registers = new vector <Register> ();
	this->indexFile = new ifstream (indexFile.c_str(), ios::in | ios::binary);
	dataFile = new ofstream (paraDocData.c_str(), ios::out | ios::binary | ios::trunc);
	FCFile = new ofstream (paraDocFC.c_str(), ios::out | ios::binary | ios::trunc);
	pointersFile = new ofstream (paraDocWords.c_str(), ios::out | ios::binary | ios::trunc);
//	namesTable (DOC_NAMES_TABLE, ios::out | ios::binary);
	pointerData = 0;
	pointerWord = 0;
	this->indexFile->read (reinterpret_cast <char*> (&wordsNumber), sizeof (unsigned long int));
	pointersFile->write(reinterpret_cast<char*>(&wordsNumber), sizeof(unsigned int));
	if (DEBUG > 0)
		cout << "------- cantidad de terminos a leer: " << wordsNumber << endl;
}

Index::~Index () {
	indexFile->close();
	dataFile->close();
	FCFile->close();
	pointersFile->close();
	delete indexFile;
	delete dataFile;
	delete FCFile;
	delete pointersFile;
	delete v_registers;
}

unsigned int Index::getWordsNumber() {
	return wordsNumber;
}

short int Index::lettersDiff (string sold, string snew) {
	unsigned int i;
	short int count = 0;
	for (i=0; (i<sold.size() && (i<snew.size()) && (sold[i] == snew[i])); i++) {
			count++;
	}
	return count;
}

void Index::readRegisters () {
	unsigned int i, j, k;
	char tam;
	unsigned int wn = floor(log2(wordsNumber));
	char buffer[1];

	v_registers->clear();

	for (i=0; i<wn; i++) {
		Register reg;
		string word = "";

		// Levanto el primer int, cantidad de documentos:
		indexFile->read (reinterpret_cast <char*> (&reg.docsNumber), sizeof(int));
		if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}

		if (indexFile->eof()) return;

		// Levanto un char, que es el tamaño del termino:
		indexFile->read (reinterpret_cast <char*> (&tam), sizeof(char));
		if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}

		// Leemos el termino y lo guardamos:
		for (j=0; j< (unsigned short int)tam; j++) {
			indexFile->read (buffer, sizeof (char));
			if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}
			word.push_back(buffer[0]);
		}
		reg.word = word;

		reg.v_data = new vector<Data>();
		// Empezamos con los datos del termino:
		for (k=0; k<reg.docsNumber; k++) {
			Data datas;
			datas.pos = new vector <unsigned int> ();

			// Levanto nº de doc.
			indexFile->read (reinterpret_cast <char*> (&datas.document), sizeof(int));
			if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}

			// Levanto frecuencia
			indexFile->read (reinterpret_cast <char*> (&datas.frecuency), sizeof (int));
			if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}

			// Levanto posiciones
			for (j=0; j<datas.frecuency; j++) {
				unsigned int n;
				indexFile->read (reinterpret_cast <char*> (&n), sizeof (int));
				if (!indexFile) { fputs ("Error al leer archivo.\n", stderr);	exit(1);}
				datas.pos->push_back(n);
			}
			reg.v_data->push_back(datas);
		}
		v_registers->push_back(reg);
	}
}

void Index::saveData (vector <Data>* v_data) {
	CodigoDelta* delta = new CodigoDelta();
	int docs = v_data->size();
	dataFile->write(reinterpret_cast <char*> (&docs), sizeof (int));
	string valoresComprimidos = delta->getCharArrayComprimido(v_data);
	for (unsigned int i=0; i<valoresComprimidos.size(); i++) {
		dataFile->write(&(valoresComprimidos.at(i)), sizeof(char));
	}
	pointerData = dataFile->tellp();
	delete delta;
}

void Index::frontCoding () {
	stringstream hexPointer;
	unsigned int i, j;
	int zero = 0;
	short int charDiff;
	unsigned int wn = log2(wordsNumber);
//	char hexa[4];
	string lastChars = "";
	pointerData = dataFile->tellp(); //archivo de datos
	pointerWord = FCFile->tellp();
	// Escribimos el puntero en hexa, al termino
	pointersFile->write(hexaToString(pointerWord).c_str(), sizeof(int));


	// Salvamos el primer termino que va completo:
	FCFile->write(reinterpret_cast <char*> (&zero), sizeof (short int));
	short int a = v_registers->at(0).word.size();
	FCFile->write(reinterpret_cast <char*> (&a), sizeof (short int));
	FCFile->write(v_registers->at(0).word.c_str(), a);
	FCFile->write(hexaToString(pointerData).c_str(), sizeof (int));


	saveData(v_registers->at(0).v_data);

	// Salvamos los demas terminos que dependen del anterior:
	for (i=1; i<wn && i<v_registers->size(); i++) {
		charDiff = lettersDiff (v_registers->at(i-1).word, v_registers->at(i).word);
		if (charDiff == 0) {
			FCFile->write(reinterpret_cast <char*> (&zero), sizeof (short int));
			short int a = v_registers->at(i).word.size();
			FCFile->write(reinterpret_cast <char*> (&a), sizeof (short int));
			FCFile->write(v_registers->at(i).word.c_str(), a);
			FCFile->write(hexaToString(pointerData).c_str(), sizeof(int));
		}
		else {
			unsigned int diff = v_registers->at(i).word.size()-charDiff;
			// Se escriben las cant de los diferentes y los nuevos
			FCFile->write(reinterpret_cast <char*> (&charDiff), sizeof (short int));
			FCFile->write(reinterpret_cast <char*> (&diff), sizeof (short int));

			// Se escriben los caracteres nuevos
			for (j=0; j<diff; j++) {
				char a = v_registers->at(i).word.at(charDiff+j);
				FCFile->write(reinterpret_cast <char*> (&a), sizeof (char));
			}
			// Se escribe el puntero en HEXADECIMAL
			FCFile->write(hexaToString(pointerData).c_str(), sizeof(int));
		}
		saveData(v_registers->at(i).v_data);
	}
}

void Index::printRegisters() {
	unsigned int i, j, k;

	for(i=0; i<v_registers->size(); i++ ) {
		printf("%d %s", v_registers->at(i).docsNumber, v_registers->at(i).word.c_str());

		for(j=0; j<v_registers->at(i).v_data->size(); j++) {
			printf(" %d %d ",v_registers->at(i).v_data->at(j).document, v_registers->at(i).v_data->at(j).frecuency);
			for(k=0; k< v_registers->at(i).v_data->at(j).pos->size(); k++) {
				printf( "%d ", v_registers->at(i).v_data->at(j).pos->at(k));
			}
		}
		printf("\n");
	}
}

void Index::run() {
	unsigned int bloques = floor(wordsNumber/floor(log2(wordsNumber)));
	for (unsigned int i=0; i<bloques; i++) {
		readRegisters();
		frontCoding();
	}
}
